package com.phychips.rcp;


import android.annotation.SuppressLint;

/**
 * RcpLib defines the internal library functions for RCP.
 */
public class RcpLib {
    static private int[] crc16tab = {
            0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
            0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
            0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
            0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
            0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
            0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
            0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
            0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
            0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
            0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
            0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
            0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
            0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
            0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
            0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
            0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
            0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
            0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
            0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
            0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
            0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
            0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
            0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
            0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
            0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
            0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
            0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
            0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
            0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
            0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
            0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
            0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
    };

    /**
     * This calculates CRC16 from the RCP packet
     *
     * @param rcpPacket : packet
     * @return CRC16
     */
    public static int calculateCRC(RcpPacket rcpPacket) {
        int i;
        int crc = 0xFFFF;

        for (i = 1; i <= (RcpPacket.PAYLOAD_INDEX + rcpPacket.payload_len); i++) {
            crc = ((crc << 8) ^ crc16tab[((crc >> 8) ^ rcpPacket.packet[i]) & 0x00FF]) & 0xFFFF;
        }

        return crc;
    }


//	/**
//	 * This calculates CRC16 from the RCP packet
//	 * @param target : packet
//	 * @return CRC16
//	 */
//	public static int calculateCRC(byte[] target)
//	{
//		int i;
//		int crc = 0xFFFF;
//		
//		for (i = 1; i < target.length; i++)
//		{
//		    crc = ((crc<<8) ^ crc16tab[((crc>>8) ^ target[i])&0x00FF]) & 0xFFFF;
//		}
//
//		/*
//		String tmpCrc = String.format("%04x", crc);
//		
//		return RcpLib.convertStringToByteArray(tmpCrc);
//		*/
//        
//		return crc;
//	}
//	// << 20141010, HYO, for test


    public static int calculateCRC(byte[] rcpPacket) {
        int i;
        int crc = 0xFFFF;
        //byte[] ba = new byte[rcpPacket.length + 1];

        //for (i = 1; i <= (RcpPacket.PAYLOAD_INDEX + rcpPacket.length); i++)
        for (i = 1; i < rcpPacket.length; i++) {
            int index = ((crc >> 8) ^ rcpPacket[i]) & 0x00FF;

            crc = ((crc << 8) ^ crc16tab[index]) & 0xFFFF;
        }

        return crc;
    }

    public static boolean Chk_CRC(int[] dest) {
        int len = dest.length;
        int CRC = ((dest[len - 2] << 8) & 0xFF00) + (dest[len - 1] & 0xFF);

        int CRCChk = Cal_CRC16(dest, len - 2);

        return (CRC == CRCChk);
    }

    public static int Cal_CRC16(int[] byteBuffer, int length) {
        int CRC = 0xFFFF;
        for (int i = 1; i < length; i++) {
            CRC = ((CRC << 8) ^ crc16tab[((CRC >> 8) ^ byteBuffer[i]) & 0x00ff]);
        }

        return CRC & 0xFFFF;
    }


    /**
     * This converts bytes array to string in decimal format.
     *
     * @param array : byte array
     * @param start : starting index
     * @param count : count to convert
     * @return converted string
     */
    public static String convertByteArrayToDecString(byte[] array, int start, int count) {
        String string;
        String result = "";
        int num;

        for (int i = start; i < start + count; i++) {
            num = RcpLib.covertUbytetoInt(array[i]);
            string = Integer.toString(num);

            result += string;
        }

        return result;
    }

    /**
     * This converts bytes array to string in hex format.
     *
     * @param array : byte array
     * @param start : starting index
     * @param count : count to convert
     * @return converted string
     */
    public static String convertByteArrayToString(byte[] array, int start, int count) {
        String string;
        String result = "";
        int num;

        for (int i = start; i < start + count; i++) {
            num = RcpLib.covertUbytetoInt(array[i]);
            string = Integer.toHexString(num);
            if (num <= 0x0F) {
                result += "0" + string;
            } else {
                result += string;
            }
        }

        return result;
    }

    /**
     * This converts bytes array to string in hex format. A space is inserted between each byte.
     *
     * @param array : byte array
     * @param start : starting index
     * @param count : count to convert
     * @return converted string
     */
    public static String convertByteArrayToSpaceString(byte[] array, int index, int length) {
        String string;
        String result = "";
        int num;

        for (int i = index; i < index + length; i++) {
            num = RcpLib.covertUbytetoInt(array[i]);
            string = Integer.toHexString(num);
            if (num <= 0x0F) {
                result += "0" + string;
            } else {
                result += string;
            }

            if (i < index + length - 1) {
                result += " ";
            }
        }

        return result;
    }

    @SuppressLint("DefaultLocale")
    public static String int2str(int[] data) {
        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < data.length; i++) {
            sb.append(Integer.toHexString((data[i] & 0xff) + 0x100).substring(1).toUpperCase());
        }
        return sb.toString();
    }

    public static byte[] int2byteArray(int[] data) {
        byte[] array = new byte[data.length];

        for (int i = 0; i < data.length; i++) {
            array[i] = (byte) (data[i] & 0xff);
        }
        return array;
    }

    @SuppressLint("DefaultLocale")
    public static String byte2string(byte data) {

        return new String(Integer.toHexString((data & 0xff) + 0x100).substring(1).toUpperCase());
    }


    @SuppressLint("DefaultLocale")
    public static String byte2str(byte[] data) {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < data.length; i++) {
            sb.append(Integer.toHexString((data[i] & 0xff) + 0x100).substring(1).toUpperCase());
        }
        return sb.toString();
    }

    public static String reverseByte2str(byte[] data) {
        StringBuilder sb = new StringBuilder();

        for (int i = data.length - 1; 0 < i; i--) {
            sb.append(Integer.toHexString((data[i] & 0xff) + 0x100).substring(1).toUpperCase());
        }
        return sb.toString();
    }

    /**
     * This converts hex string to byte array.
     *
     * @param string : hex string
     * @return byte array
     */
    public static byte[] convertStringToByteArray(String string) {
        int length = string.length();
        int number1, number2;
        byte[] array = new byte[length / 2];

        for (int i = 0; i < length; i += 2) {
            number1 = Integer.parseInt(string.substring(i, i + 1), 16);
            number2 = Integer.parseInt(string.substring(i + 1, i + 2), 16);

            array[i / 2] = (byte) (((number1 << 4) + number2) & 0xFF);
        }

        return array;
    }

    /**
     * This converts hex string to byte array. The string has a space between each byte.
     *
     * @param string : hex string
     * @return byte array
     */
    public static byte[] convertSpaceStringToByteArray(String string) {
        int length = string.length();
        int number1, number2;
        byte[] array = new byte[(length + 1) / 3];

        for (int i = 0; i < length; i += 3) {
            number1 = Integer.parseInt(string.substring(i, i + 1), 16);
            number2 = Integer.parseInt(string.substring(i + 1, i + 2), 16);

            array[i / 3] = (byte) (((number1 << 4) + number2) & 0xFF);
        }

        return array;
    }

    /**
     * This decode payload (byte array) to ascii string.
     *
     * @param rcpPacket : response packet
     * @return string
     */
    public static String payloadToString(RcpPacket rcpPacket) {
        String string;
        char[] payload = new char[rcpPacket.payload_len];

        for (int i = 0; i < rcpPacket.payload_len; i++) {
            payload[i] = (char) rcpPacket.packet[RcpPacket.PAYLOAD_INDEX + i];
        }

        string = new String(payload);
        return string;
    }

    /**
     * This converts byte to short. The byte value is regarded as unsigned.
     *
     * @param number : byte value
     * @return short value
     */
    public static short covertUbytetoShort(byte number) {
        short result = number;

        if (result < 0) {
            result += Byte.MAX_VALUE;
        }
        return result;
    }

    /**
     * This converts byte to int. The byte value is regarded as unsigned.
     *
     * @param number : byte value
     * @return int value
     */
    public static int covertUbytetoInt(byte number) {
        int result = number;

        if (result < 0) {
            result += 0x100;
        }
        return result;
    }

    /**
     * This converts short to int. The short value is regarded as unsigned.
     *
     * @param number : short value
     * @return int value
     */
    public static int covertUShorttoInt(short number) {
        int result = number;

        if (result < 0) {
            result += 0x10000;
        }
        return result;
    }

    /**
     * This converts short to byte.
     *
     * @param number : short value
     * @return byte value
     */
    public static byte covertShorttoByte(short number) {
        byte result;
        result = (byte) (number & 0xFF);
        return result;
    }

    /**
     * This extracts partial bits from the integer value.
     *
     * @param number : integer value
     * @param index  : starting index. The index starts from LSB.
     * @param count  : bit count to extract
     * @return : extracted value
     * @throws RcpException
     */
    public static int getPartialBits(int number, int index, int count) throws RcpException {
        int bitmask = 1, i, result = number;
        RcpException exception;

        if ((count <= 0 || count >= 32) ||
                (index < 0 || index >= 32)) {
            exception = new RcpException("invalid argument", RcpConst.INVALID_ARGUMENT);
            throw exception;
        }

        for (i = 0; i < count - 1; i++) {
            bitmask <<= 1;
            bitmask += 1;
        }
        bitmask <<= index;

        result = result & bitmask;
        result >>= index;

        return result;
    }

    /**
     * This converts byte array to long value
     *
     * @param array : byte array
     * @param index : starting index
     * @param count : count to convert
     * @return converted value
     * @throws RcpException
     */
    public static long convertByteArrayToLong(byte[] array, int index, int count) throws RcpException {
        int i;
        long result = 0;
        RcpException exception;

        if ((count <= 0 || count > 8) ||
                (index < 0) ||
                (array.length < (index + count))) {
            exception = new RcpException("invalid argument", RcpConst.INVALID_ARGUMENT);
            throw exception;
        }

        for (i = 0; i < count - 1; i++) {
            result += covertUbytetoInt(array[index + i]);
            result <<= 8;
        }
        result += covertUbytetoInt(array[index + i]);

        return result;
    }

    /**
     * This converts byte array to int value
     *
     * @param array : byte array
     * @param index : starting index
     * @param count : count to convert
     * @return converted value
     * @throws RcpException
     */
    public static int convertByteArrayToInt(byte[] array, int index, int count) throws RcpException {
        int i;
        int result = 0;
        RcpException exception;

        if ((count <= 0 || count > 4) ||
                (index < 0) ||
                (array.length < (index + count))) {
            exception = new RcpException("invalid argument", RcpConst.INVALID_ARGUMENT);
            throw exception;
        }

        for (i = 0; i < count - 1; i++) {
            result += covertUbytetoInt(array[index + i]);
            result <<= 8;
        }
        result += covertUbytetoInt(array[index + i]);

        return result;
    }

    /**
     * This converts long value to byte array.
     *
     * @param number : long value
     * @param array  : destination byte array
     * @param index  : starting index
     * @param count  : count to convert
     * @throws RcpException
     */
    public static void convertLongToByteArray(long number, byte[] array, int index, int count) throws RcpException {
        int i;
        RcpException exception;

        if ((count <= 0 || count > 8) ||
                (number < 0) ||
                (index < 0) ||
                (array.length < (index + count))) {
            exception = new RcpException("invalid argument", RcpConst.INVALID_ARGUMENT);
            throw exception;
        }

        for (i = count - 1; i >= 0; i--) {
            array[index + i] = (byte) (number & 0xFF);
            number = number >> 8;
        }

        return;
    }

    /**
     * This converts int value to byte array.
     *
     * @param number : int value
     * @param array  : destination byte array
     * @param index  : starting index
     * @param count  : count to convert
     * @throws RcpException
     */
    public static void convertIntToByteArray(int number, byte[] array, int index, int count) throws RcpException {
        int i;
        byte num;

        for (i = count - 1; i >= 0; i--) {
            //System.out.print(Integer.toHexString(number) + " ");
            num = (byte) (number & 0xFF);
            array[index + i] = num;
            number = number >> 8;
        }

        return;
    }

    /**
     * This copies byte array.
     *
     * @param source       : byte array
     * @param source_index : starting index
     * @param dest         : byte array
     * @param dest_index   : starting index
     * @param count        : count to copy
     * @throws RcpException
     */
    public static void copyByteArray(byte[] source, int source_index, byte[] dest, int dest_index, int count) throws RcpException {
        int i;
        //RcpException		exception;

        if (count == 0) {
            return;
        }

//		if ( (count < 0) ||
//			 (source_index < 0) ||
//			 (dest_index < 0) ||
//			 (source.length < (source_index + count)) ||
//			 (dest.length < (dest_index + count)) ) 
//		{
//			exception = new RcpException("invalid argument", RcpConst.INVALID_ARGUMENT);
//			throw exception;		
//		}	

        for (i = 0; i < count; i++) {
            dest[dest_index + i] = source[source_index + i];
        }

        return;
    }

}
